const models = require("./Models");
const lodash = require("lodash");
const Shexian = models.Shexian.prototype;
const Point = models.Point.prototype;
// let s = new Shexian();
// (new Shexian())
// debugger

let repeat = (str, times) => {
  return (new Array(times)).fill(str).join("")
}

/**
 * @returns {Array<Set<Shexian>>}
 * @param {Shexian[]} shexians 
 * @param {Number} epsilon 
 * @param {Number} min_pts 
 */
function dbscan_myversion(shexians, epsilon = 0.5, min_pts = 3) {
  // debugger
  // let clusterId = 0;
  let all_distances = [];
  for (let i = 0; i < shexians.length; i++) {
    let arr = [];
    for (let ii = 0; ii < shexians.length; ii++) {
      arr.push(-1);
    }
    all_distances.push(arr);
  }
  // let x = all_distances[0]
  for (let i = 0; i < shexians.length; i++) {
    for (let j = 0; j < shexians.length && j != i; j++) {
      if (all_distances[i][j] != -1) {
        continue;
      }
      let d = shexians[i].myDistanceTo(shexians[j]);
      all_distances[i][j] = d;
      all_distances[j][i] = d;
      // debugger
    }
  }
  // all_distances[0]
  let corePointIndexes = [];
  for (let i = 0; i < shexians.length; i++) {
    if (all_distances[i].filter(e => e != Infinity && e >= 0 && e < epsilon).length > min_pts) {
      // debugger
      corePointIndexes.push(i);
    }
  }
  const MARKS = {
    "unclassified": "unclassiØed",
    // "iscore": "iscore",
    'clustered': "clustered"
  }
  shexians.forEach(e => e.__info = MARKS.unclassified);
  let clusters = []
  for (let cpi of corePointIndexes) {
    let core = shexians[cpi];
    if (core.__info == MARKS.clustered) {
      continue;
    }
    /**@type {Set<Shexian>} */
    let cluster = new Set;


    /**
     * 
     * @param {Shexian} a_core 
     */
    let expand = (a_core_index, cengci = 0) => {
      let a_core = shexians[a_core_index];
      // a_core.__info = MARKS.clustered;
      // cluster.add(a_core);
      console.log(repeat(" ", cengci), "start expand ", a_core_index)
      let childrenIndexes = shexians.map((e, i) => { return { e: e, i } }).filter((x) => {
        // debugger
        let e = x.e;
        let i = x.i
        return e.__info == MARKS.unclassified && all_distances[a_core_index][i] < epsilon && all_distances[a_core_index][i] >= 0
      });
      // debugger
      if (childrenIndexes.length == 0) {
        // debugger
      }
      console.log(repeat(" ", cengci), "next..", childrenIndexes.map(e => e.i).join(" | "))
      for (let x of childrenIndexes) {
        cluster.add(x.e);
        x.e.__info = MARKS.clustered;
      }
      for (let x of childrenIndexes) {
        expand(x.i, cengci + 1);
      }
      // debugger
    }
    cluster.add(core);
    core.__info = MARKS.clustered;
    expand(cpi);
    // debugger

    /**
     * @description 检查下这个簇里面的线包含了多少条路径
     */
    let traj_indexes = Array.from(cluster).map(e => e.trajectory_id);
    let uniq_traj_indexes = lodash.uniq(traj_indexes);
    if (uniq_traj_indexes.length >= 3) {
      clusters.push(cluster);
    } else {
      debugger
      cluster.forEach(e => {
        e.__info = MARKS.unclassified;
      })
    }
    // debugger


  }

  // debugger
  return clusters;



}

/**
 * @returns {Array<Set<Shexian>>}
 * @param {Shexian[]} shexians 
 * @param {Number} epsilon 
 * @param {Number} min_pts 
 */
function cluster924(shexians, epsilon = 0.5, min_pts = 3) {
  // debugger
  // let clusterId = 0;
  /**@type {Array<{angle:{
   * cos:Number,
   * sin_square:Number,
   * cos_square:Number
   * },endpointDistance:Number}>} */
  let all_distances = [];
  for (let i = 0; i < shexians.length; i++) {
    let arr = [];
    for (let ii = 0; ii < shexians.length; ii++) {
      arr.push(null);
    }
    all_distances.push(arr);
  }
  // let x = all_distances[0]
  for (let i = 0; i < shexians.length; i++) {
    for (let j = 0; j < shexians.length && j != i; j++) {
      if (all_distances[i][j] != null) {
        continue;
      }
      // let d = shexians[i].myDistanceTo(shexians[j]);
      let angle = shexians[i].angleTo(shexians[j]);
      let endpointDistance = shexians[i].endpointDistanceToV2(shexians[j]);
      let d = { angle, endpointDistance }
      all_distances[i][j] = d;
      all_distances[j][i] = d;
      // debugger
    }
  }
  all_distances[0]
  // debugger
  let corePointIndexes = [];
  for (let i = 0; i < shexians.length; i++) {
    if (all_distances[i].filter(e =>
      e
      && e.angle
      && e.angle.cos >= Math.cos(Math.PI / 6)
      && e.endpointDistance
      && e.endpointDistance.d_min <= 10000
      // && e.endpointDistance.d_start <= 10000
      // && e.endpointDistance.d_end <= 10000
    ).length > min_pts) {
      // debugger
      corePointIndexes.push(i);
    }
  }
  // debugger
  const MARKS = {
    "unclassified": "unclassiØed",
    // "iscore": "iscore",
    'clustered': "clustered"
  }
  shexians.forEach(e => e.__info = MARKS.unclassified);

  let clusters = []
  for (let cpi of corePointIndexes) {
    let core = shexians[cpi];
    if (core.__info == MARKS.clustered) {
      continue;
    }
    /**@type {Set<Shexian>} */
    let cluster = new Set;

    let childrenIndexes = shexians.map((ele, index) => { return { ele, index } }).filter((x) => {
      // debugger
      let i = x.index
      return x.ele.__info == MARKS.unclassified && all_distances[cpi][i] && all_distances[cpi][i].angle.cos >= Math.cos(Math.PI / 6)
        && all_distances[cpi][i].endpointDistance
        && all_distances[cpi][i].endpointDistance.d_end <= 10000
        && all_distances[cpi][i].endpointDistance.d_start <= 10000

    });
    for (let x of childrenIndexes) {
      cluster.add(x.ele);
      x.ele.__info = MARKS.clustered;
    }
    // debugger
    let traj_indexes = Array.from(cluster).map(e => e.trajectory_id);
    let uniq_traj_indexes = lodash.uniq(traj_indexes);
    if (uniq_traj_indexes.length >= 3) {
      clusters.push(cluster)
    } else {
      cluster.forEach(e => {
        e.__info = MARKS.unclassified;
      })
    }

  }

  debugger
  return clusters;



}




module.exports = {
  dbscan_myversion,
  cluster924
}